/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2015-2018. All rights reserved.
 * Description: Signature Verify CBB Library
 * Author: tangzonglei
 * Create: 2015
 * History: 2018/11/23 yebin code rule fixes
 */
#include "../cms/cmscbb_cms_parse.h"
#include "cmscbb_sdk.h"
#include "../asn1/cmscbb_asn1_decode.h"
#include "../asn1/cmscbb_asn1_utils.h"
#include "../cmscbb_common/cmscbb_common.h"
#include "../cmscbb_common/cmscbb_def.h"
#include "../cmscbb_common/cmscbb_list.h"
#include "../x509/cmscbb_x509.h"
/*
 * Prototype    : InternalDecodeCert
 * Description  : Decode certificate in CMS
 * Params
 *   [IN] asnOcts: asn objects
 *   [OUT] ppCert: X509 certificates
 *   [OUT] bytesDecoded: decoded result
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 20:16  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalDecodeCertByAsnOcts(const CmscbbAsnOcts* asnOcts, CmscbbX509Cert** ppCert,
    CVB_UINT32* bytesDecoded)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbBerTag tagExpect;
    CmscbbAsn1Item* item = CmscbbGetCmscbbX509Cert();

    tagExpect.cls = item->berClass;
    tagExpect.form = item->berForm;
    tagExpect.code = item->berCode;

    if (CmscbbMemCmp(&(tagExpect), &(asnOcts->tag), sizeof(CmscbbBerTag)) != 0) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CERT_DECODE, CVB_NULL);
        return (CMSCBB_ERR_PKI_CERT_DECODE);
    }

    ret = CmscbbX509DecodeCert(asnOcts->octs, asnOcts->len, ppCert, bytesDecoded);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalDecodeCert
 * Description  : Decode certificate in CMS
 * Params
 *   [IN] rawCerts:X509 certificate raw bundle
 *   [IN] certs: X509 certificates
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 20:16  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalDecodeCert(const CmscbbX509CertRawBundle* rawCerts, CmscbbX509CertBundle* certs)
{
    CMSCBB_ERROR_CODE ret = CVB_SUCCESS;
    CVB_INT iter;

    for (iter = 0; iter < (CVB_INT)rawCerts->bundle.num; ++iter) {
        CmscbbX509Cert* cert = CVB_NULL;
        CVB_UINT32 nDecoded = 0;
        ret = InternalDecodeCertByAsnOcts(&(rawCerts->bundle.data[iter]), &cert, &nDecoded);
        if (CVB_FAILED(ret)) {
            CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_DECODE, "Can't decode cert in cms file");
            break;
        }

        ret = CMSCBB_LIST_ADD(&(certs->certs), cert);
        if (CVB_FAILED(ret)) {
            CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_DECODE, "Can't add cert to certs' list");
            CmscbbX509FreeCert(cert);
            break;
        }
    }

    if (ret != CVB_SUCCESS) {
        CMSCBB_LIST_FREE(certs, CmscbbX509FreeCert);
        return CMSCBB_ERR_PKI_CMS_DECODE;
    }

    return CVB_SUCCESS;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalDecodeCrlByAsnOcts(const CmscbbAsnOcts* asnOcts, CmscbbX509Crl** crl,
    CVB_UINT32* bytesDecoded)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbBerTag tagExpect;
    CmscbbAsn1Item* item = CmscbbGetCmscbbX509Crl();

    tagExpect.cls = item->berClass;
    tagExpect.form = item->berForm;
    tagExpect.code = item->berCode;

    if (CmscbbMemCmp(&(tagExpect), &(asnOcts->tag), sizeof(CmscbbBerTag)) != 0) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CERT_DECODE, CVB_NULL);
        return (CMSCBB_ERR_PKI_CERT_DECODE);
    }

    ret = CmscbbX509DecodeCrl(asnOcts->octs, asnOcts->len, crl, bytesDecoded);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalDecodeCrl
 * Description  : Decode crl in CMS
 * Params
 *   [IN] rawCrls:X509 crl raw bundle
 *   [IN] crls: X509 crl
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2020/12/2 11:30  zhanghui  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalDecodeCrl(const CmscbbX509CrlRawBundle* rawCrls, CmscbbX509CrlBundle* crls)
{
    CMSCBB_ERROR_CODE ret = CVB_SUCCESS;
    CVB_INT iter;

    for (iter = 0; iter < (CVB_INT)rawCrls->bundle.num; ++iter) {
        CmscbbX509Crl* crl = CVB_NULL;
        CVB_UINT32 nDecoded = 0;
        ret = InternalDecodeCrlByAsnOcts(&(rawCrls->bundle.data[iter]), &crl, &nDecoded);
        if (CVB_FAILED(ret)) {
            CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_DECODE, "Can't decode crl in cms file");
            break;
        }

        ret = CMSCBB_LIST_ADD(&(crls->crls), crl);
        if (CVB_FAILED(ret)) {
            CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_DECODE, "Can't add crl to certs' list");
            CmscbbX509FreeCrl(crl);
            break;
        }
    }

    if (ret != CVB_SUCCESS) {
        CMSCBB_LIST_FREE(crls, CmscbbX509FreeCrl);
        return CMSCBB_ERR_PKI_CMS_DECODE;
    }

    return CVB_SUCCESS;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckOID(CmscbbPkcs7Content* dest)
{
    CMSCBB_ERROR_CODE ret;
    const CmscbbAsnOid* signedData = CmscbbFindOid(AOID_PKCS7_SIGNEDDATA);

    /* check signedData Object identifier */
    ret = (CVB_UINT32)CMSCBB_COMPARE_ASN_OID(&(dest->contentType), signedData);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_DECODE, "SignedData's OID error!");
        return CMSCBB_ERR_PKI_CMS_DECODE;
    }

    return CVB_SUCCESS;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckVersion(CVB_INT32 cmsVersion)
{
    /* for signedData, The current cbb library only supports version 1 and 3 verification */
    if (cmsVersion != 1 && cmsVersion != 3) { /* 1 3 means version of cbb library */
        return CMSCBB_ERR_PKI_CMS_DECODE;
    }
    return CVB_SUCCESS;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckOIDAndVersion(CmscbbPkcs7Content* dest)
{
    CMSCBB_ERROR_CODE ret;
    SET_OF(CmscbbPkcs7SignedInfo)* signerInfos = CVB_NULL;
    CVB_UINT32 iter;
    ret = InternalCheckOID(dest);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }
    ret = InternalCheckVersion(dest->signedData.version);
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, "SignedData version error.");
        return ret;
    }
    signerInfos = &(dest->signedData.signerInfos.infos);
    for (iter = 0; iter < signerInfos->num; iter++) {
        const CmscbbPkcs7SignedInfo* info = &signerInfos->data[iter];
        ret = InternalCheckVersion(info->version);
        if (CVB_FAILED(ret)) {
            CVB_LOG_ERROR(ret, "signerInfos version error.");
            return ret;
        }
    }
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbPkcs7DecodeSigned(const CVB_BYTE* encodedP7, CVB_UINT32 encodedLength,
    CmscbbPkcs7Content** sign, CVB_UINT32* bytesDecoded)
{
    CmscbbPkcs7Content* dest = CVB_NULL;
    CMSCBB_ERROR_CODE ret;
    CmscbbX509CertRawBundle* rawCerts = CVB_NULL;
    CmscbbX509CrlRawBundle* rawCrls = CVB_NULL;
    CmscbbAsn1Item *pkcs7Content = CVB_NULL;

    if ((encodedP7 == CVB_NULL) || (encodedLength == 0) || (sign == CVB_NULL) || (bytesDecoded == CVB_NULL)) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    pkcs7Content = CmscbbGetCmscbbPkcs7Content();
    ret = CmscbbAsnDecode(encodedP7, encodedLength, pkcs7Content, CVB_ASN_NORMAL,
        (CVB_VOID**)(&dest), bytesDecoded);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CMS_DECODE, CVB_NULL);
        return (CMSCBB_ERR_PKI_CMS_DECODE);
    }

    ret = InternalCheckOIDAndVersion(dest);
    if (CVB_FAILED(ret)) {
        CmscbbPkcs7FreeSigned(dest);
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }
    rawCerts = &(dest->signedData.rawCerts);
    if (rawCerts->bundle.num > 0) {
        ret = InternalDecodeCert(rawCerts, &(dest->signedData.certificates));
        if (CVB_FAILED(ret)) {
            CmscbbPkcs7FreeSigned(dest);
            return CMSCBB_ERR_PKI_CMS_DECODE;
        }
    }
    rawCrls = &(dest->signedData.rawCrls);
    if (rawCrls->bundle.num > 0) {
        ret = InternalDecodeCrl(rawCrls, &(dest->signedData.crls));
        if (CVB_FAILED(ret)) {
            CmscbbPkcs7FreeSigned(dest);
            return CMSCBB_ERR_PKI_CMS_DECODE;
        }
    }
#ifdef CVB_DEBUG
    CVB_INT iter;
    for (iter = 0; iter < (CVB_INT)dest->signedData.signerInfos.infos.num; ++iter) {
        CmscbbPkcs7SignedInfo* si = &(dest->signedData.signerInfos.infos.data[iter]);
        CVB_UINT32 nameLen = 0;
        (CVB_VOID)CmscbbConvertFromX509Name(&(si->issuerSn.issuer), &si->issuerName, &nameLen);
    }
#endif /* CVB_DEBUG */

    *sign = (CmscbbPkcs7Content*)dest;
    return CVB_SUCCESS;
}

CVB_VOID CmscbbPkcs7FreeSigned(CmscbbPkcs7Content* freeSigned)
{
    if (freeSigned == CVB_NULL) {
        return;
    }

#ifdef CVB_DEBUG
    CVB_INT iter;
    for (iter = 0; iter < (CVB_INT)freeSigned->signedData.signerInfos.infos.num; ++iter) {
        CmscbbPkcs7SignedInfo* si = &(freeSigned->signedData.signerInfos.infos.data[iter]);
        CmscbbFreeWithNullCheck(si->issuerName);
    }
#endif /* CVB_DEBUG */

    CMSCBB_LIST_FREE(&(freeSigned->signedData.certificates.certs), CmscbbX509FreeCert);
    CMSCBB_LIST_FREE(&(freeSigned->signedData.crls.crls), CmscbbX509FreeCrl);

    CmscbbFreeWithNullCheck(freeSigned);
}
